
'''
    Software License

    Copyright (C) 2021-05-24  Xoronos

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, version 3.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
'''

'''
    Liabilities

    The software is provided "AS IS" without any warranty of any kind, either expressed,
    implied, or statutory, including, but not limited to, any warranty that the software
    will conform to specifications, any implied warranties of merchantability, fitness
    for a particular purpose, and freedom from infringement, and any warranty that the
    documentation will conform to the software, or any warranty that the software will
    be error free.

    In no event shall Xoronos be liable for any damages, including, but not limited to,
    direct, indirect, special or consequential damages, arising out of, resulting from,
    or in any way connected with this software, whether or not based upon warranty,
    contract, tort, or otherwise, whether or not injury was sustained by persons or
    property or otherwise, and whether or not loss was sustained from, or arose out of
    the results of, or use of, the software or services provided hereunder.
    
    To request the provided software under a different license you can contact us at
    support@xoronos.com
'''

#####################################################
##                   Introduction                  ##
#####################################################
## This code example shows how to generate and     ##
## merge blocks of data. Furtermore, the code      ##
## example shows also how to check if a block      ##
## contains the entire file, and how to strip the  ##
## block header.                                   ##
#####################################################

import xrnlib256 as xrn
import random
import lorem_ipsum as li



iblock0_lenp = xrn.new_uint64p()
iblock1_lenp = xrn.new_uint64p()
oblock_lenp = xrn.new_uint64p()
stripb_lenp = xrn.new_uint64p()

#############
## Logging ##
#############

# Set the default logging file streams
# errors -> stderr
# warnings -> stdout
# notifications -> stdout
# debug -> stdout

xrn.xrn_set_default_log(  )

# To change the default logging you can use the functions below,
# where fp is the opened file pointer
# 
# xrn_change_error_log( fp )
# xrn_change_warning_log( fp )
# xrn_change_notification_log( fp )
# xrn_change_debug_log( fp )

tag = random.randint(0,2**32-1)

########################################
## get sizes for each stream of bytes ##
########################################

offset_first_block = 0
length_first_block = random.randint(0,len(li.LOREM_IPSUM_STR ) )
offset_second_block = length_first_block
length_second_block = len( li.LOREM_IPSUM_STR ) - offset_second_block

if ( xrn.XSUCCESS != xrn.xrn_estimate_block_bytes_length( length_first_block,    # the input length of data in the first block
                                     iblock0_lenp   # the output total length of the first block
      ) ) :
    exit ( - 1 )

if ( xrn.XSUCCESS != xrn.xrn_estimate_block_bytes_length( length_second_block,   # the input length of data in the second block 
                                     iblock1_lenp   # the output total length of the second block
      ) ) :
    exit ( - 1 )

num_blocks = 2
blocks_len = xrn.new_uint64p_array(num_blocks)
if ( blocks_len == None ) :
    exit ( - 1 )

xrn.uint64p_array_setitem( blocks_len, 0, xrn.uint64p_value(iblock0_lenp) )
xrn.uint64p_array_setitem( blocks_len, 1, xrn.uint64p_value(iblock1_lenp) )

if ( xrn.XSUCCESS != xrn.xrn_estimate_block_bytes_length_merge( blocks_len,     # the input lengths
                                                                num_blocks,     # the number of inputs
                                                                oblock_lenp     # the output total length of the merged blocks
      ) ) :
    exit ( - 1 )
    xrn.delete_uint64p_array( blocks_len )

xrn.delete_uint64p_array( blocks_len )


if ( xrn.XSUCCESS != xrn.xrn_estimate_block_bytes_length_strip( xrn.uint64p_value(oblock_lenp),      # the input block total length
                                           stripb_lenp      # the output length of data without metadata
      ) ) :
    exit ( - 1 )


iblock0_len = xrn.uint64p_value(iblock0_lenp)
iblock1_len = xrn.uint64p_value(iblock1_lenp) 
oblock_len = xrn.uint64p_value(oblock_lenp)
stripb_len = xrn.uint64p_value(stripb_lenp) 
xrn.delete_uint64p( iblock0_lenp )
xrn.delete_uint64p( iblock1_lenp )
xrn.delete_uint64p( oblock_lenp )
xrn.delete_uint64p( stripb_lenp )

iblock0 = xrn.new_uint8p(  iblock0_len )
iblock1 = xrn.new_uint8p(  iblock1_len )
oblock = xrn.new_uint8p( oblock_len )
stripb = xrn.new_uint8p( stripb_len )

###################
## create blocks ##
###################

print( "create first block...\n" )
if ( xrn.XSUCCESS != xrn.xrn_make_block_bytes( li.LOREM_IPSUM_ARRAY,     # the input bytes
                                         len( li.LOREM_IPSUM_STR ), # the input bytes length
                                         iblock0,   # the output block bytes
                                         iblock0_len,       # the output block length
                                         tag,       # the tag of the block
                                         offset_first_block,        # the offset to start create the block
                                         length_first_block # the length of bytes to be put in the block
      ) ) :
    xrn.delete_uint8p( iblock0 )
    xrn.delete_uint8p( iblock1 )
    xrn.delete_uint8p( oblock )
    xrn.delete_uint8p( stripb )
    exit ( - 1 )

print( "create second block...\n" )
if ( xrn.XSUCCESS != xrn.xrn_make_block_bytes( li.LOREM_IPSUM_ARRAY, # the input bytes
                                         len( li.LOREM_IPSUM_STR ),  # the input bytes length
                                         iblock1,                    # the output block bytes 
                                         iblock1_len,                # the output block length
                                         tag,                        # the tag of the block 
                                         offset_second_block,        # the offset to start create the block
                                         length_second_block         # the length of bytes to be put in the block
      ) ) :
    xrn.delete_uint8p( iblock0 )
    xrn.delete_uint8p( iblock1 )
    xrn.delete_uint8p( oblock )
    xrn.delete_uint8p( stripb )
    exit ( - 1 )

#################
## merge bytes ##
#################

num_blocks = 2
blocks = xrn.new_uint8p_array(xrn.XRN_MAX_MERGE_PARALLELISM)

if ( blocks == None ) :
    xrn.delete_uint8p( iblock1 )
    xrn.delete_uint8p( iblock0 )
    xrn.delete_uint8p( iblock1 )
    xrn.delete_uint8p( oblock )
    xrn.delete_uint8p( stripb )
    exit( -1 )

blocks_len = xrn.new_uint64p_array(xrn.XRN_MAX_MERGE_PARALLELISM)

if ( blocks_len == None ) :
    xrn.delete_uint8p( iblock1 )
    xrn.delete_uint8p( iblock0 )
    xrn.delete_uint8p( iblock1 )
    xrn.delete_uint8p( oblock )
    xrn.delete_uint8p( stripb )
    xrn.delete_uint8_array( blocks )
    exit( -1 )

xrn.uint8p_array_setitem( blocks, 0, iblock0 )
xrn.uint8p_array_setitem( blocks, 1, iblock1 )
xrn.uint64p_array_setitem( blocks_len, 0, xrn.uint64p_value(iblock0_lenp) )
xrn.uint64p_array_setitem( blocks_len, 1, xrn.uint64p_value(iblock1_lenp) )

print( "merge the two blocks...\n" )
if ( xrn.XSUCCESS != xrn.xrn_join_blocks_bytes( blocks, # the pointers to the input block streams
                                         blocks_len,    # the stream of block bytes lengths
                                         num_blocks,    # the number of blocks provided
                                         oblock,        # the output block of bytes 
                                         oblock_len     # the output block of bytes length
      ) ) :
    xrn.delete_uint8p( iblock1 )
    xrn.delete_uint8p( iblock0 )
    xrn.delete_uint8p( iblock1 )
    xrn.delete_uint8p( oblock )
    xrn.delete_uint8p( stripb )
    xrn.delete_uint8p_array( blocks )
    xrn.delete_uint64p_array( blocks_len )
    exit ( - 1 )

xrn.delete_uint8p_array( blocks )
xrn.delete_uint64p_array( blocks_len )

###################################
## check if the file is complete ##
###################################

print( "check if the block contains the entire information...\n" )
if ( xrn.XNCOMPLETE == xrn.xrn_check_complete_blocks_bytes( oblock, # the input block of bytes
                                                    oblock_len      # the input block of bytes length
      ) ) :
    xrn.delete_uint8p( iblock0 )
    xrn.delete_uint8p( iblock1 )
    xrn.delete_uint8p( oblock )
    xrn.delete_uint8p( stripb )
    exit ( - 1 )

##################
## strip header ##
##################

print( "strip the header from the block file...\n" )
if ( xrn.XSUCCESS != xrn.xrn_strip_header_from_block_bytes( oblock,      # the input block of bytes
                                               oblock_len,  # the input block of bytes length
                                               stripb,      # the output bytes stripped from metadata
                                               stripb_len   # the output bytes stripped from metadata length
      ) ) :
    xrn.delete_uint8p( iblock0 )
    xrn.delete_uint8p( iblock1 )
    xrn.delete_uint8p( oblock )
    xrn.delete_uint8p( stripb )
    exit ( - 1 )

##############################
## writing bytes into files ##
##############################

block0_fp = xrn.fopen( "block0.txt", "wb" )
block1_fp = xrn.fopen( "block1.txt", "wb" )
merged_fp = xrn.fopen( "merged.txt", "wb" )
stripped_fp = xrn.fopen( "stripped.txt", "wb" )

if ( ( block0_fp == None ) or ( block1_fp == None ) or ( merged_fp == None ) or ( stripped_fp == None ) ) :
    xrn.delete_uint8p( iblock0 )
    xrn.delete_uint8p( iblock1 )
    xrn.delete_uint8p( oblock )
    xrn.delete_uint8p( stripb )
    exit ( - 1 )

if ( 1 != xrn.fwrite( iblock0, iblock0_len, 1, block0_fp ) ) :
    xrn.delete_uint8p( iblock0 )
    xrn.delete_uint8p( iblock1 )
    xrn.delete_uint8p( oblock )
    xrn.delete_uint8p( stripb )
    xrn.fclose( block0_fp )
    xrn.fclose( block1_fp )
    xrn.fclose( merged_fp )
    xrn.fclose( stripped_fp )
    exit ( - 1 )

if ( 1 != xrn.fwrite( iblock1, iblock1_len, 1, block1_fp ) ) :
    xrn.delete_uint8p( iblock0 )
    xrn.delete_uint8p( iblock1 )
    xrn.delete_uint8p( oblock )
    xrn.delete_uint8p( stripb )
    xrn.fclose( block0_fp )
    xrn.fclose( block1_fp )
    xrn.fclose( merged_fp )
    xrn.fclose( stripped_fp )
    exit ( - 1 )

if ( 1 != xrn.fwrite( oblock, oblock_len, 1, merged_fp ) ) :
    xrn.delete_uint8p( iblock0 )
    xrn.delete_uint8p( iblock1 )
    xrn.delete_uint8p( oblock )
    xrn.delete_uint8p( stripb )
    xrn.fclose( block0_fp )
    xrn.fclose( block1_fp )
    xrn.fclose( merged_fp )
    xrn.fclose( stripped_fp )
    exit ( - 1 )

if ( 1 != xrn.fwrite( stripb, stripb_len, 1, stripped_fp ) ) :
    xrn.delete_uint8p( iblock0 )
    xrn.delete_uint8p( iblock1 )
    xrn.delete_uint8p( oblock )
    xrn.delete_uint8p( stripb )
    xrn.fclose( block0_fp )
    xrn.fclose( block1_fp )
    xrn.fclose( merged_fp )
    xrn.fclose( stripped_fp )
    exit ( - 1 )

xrn.fclose( block0_fp )
xrn.fclose( block1_fp )
xrn.fclose( merged_fp )
xrn.fclose( stripped_fp )

xrn.delete_uint8p( iblock0 )
xrn.delete_uint8p( iblock1 )
xrn.delete_uint8p( oblock )
xrn.delete_uint8p( stripb )

exit ( 0 )


